package com.tbynet.jwp.framework.core;

import com.jfinal.log.Log;
import com.jfinal.plugin.activerecord.Page;
import com.jfinal.plugin.activerecord.Record;
import com.jfinal.plugin.activerecord.Db;
import com.tbynet.jwp.framework.exception.RepositoryException;

import java.util.List;
import java.util.function.Supplier;

/**
 * 查询仓储基类 - 封装通用读操作（完全适配JFinal原生API）
 * 核心修正：通过反射调用Model的dao()方法获取单例，调用原生方法查询Model，而非Db静态方法
 * @param <M> 泛型：JwpModel子类，对应数据库表
 */
public abstract class JwpQueryRepository<M extends JwpModel<M>> {
    /**
     * 日志工具
     */
    protected final Log log = Log.getLog(getClass());
    /**
     * 当前仓储对应的Model类
     */
    protected final Class<M> modelClass;
    /**
     * 调用Model的dao()方法获取单例（通过反射获取）
     */
    protected final M modelDao;
    /**
     * 慢查询阈值（ms）
     */
    protected static final long SLOW_QUERY_THRESHOLD = 500;

    /**
     * 构造器：初始化Model类并反射获取dao单例
     * @param modelClass Model的Class对象
     */
    public JwpQueryRepository(Class<M> modelClass) {
        this.modelClass = modelClass;
        // 反射调用Model的dao()方法获取单例（核心修正点）
        this.modelDao = getModelDaoInstance();
    }

    // -------------------------- 核心工具方法：反射调用Model的dao()方法获取单例 --------------------------
    /**
     * 通过JFinal Model的原生dao()方法获取单例（替代反射静态字段）
     * 更贴合JFinal设计，避免静态字段未定义的异常
     * @return Model的dao单例
     */
    private M getModelDaoInstance() {
        try {
            // 步骤1：通过反射实例化Model（无参构造）
            M modelInstance = modelClass.getDeclaredConstructor().newInstance();
            // 步骤2：调用Model的dao()方法获取单例（核心修正点）
            return modelInstance.dao();
        } catch (Exception e) {
            String errorMsg = String.format("获取%s的dao单例失败，原因：%s", modelClass.getSimpleName(), e.getMessage());
            log.error(errorMsg, e);
            throw new RuntimeException(errorMsg);
        }
    }

    // -------------------------- 异常包装模板方法 --------------------------
    /**
     * 读操作异常包装模板：捕获数据库异常，包装为RepositoryException抛出
     * @param action 读操作逻辑
     * @param errorMsg 异常提示信息
     * @param <T> 操作返回值类型
     * @return 操作结果
     */
    protected <T> T executeReadOperation(Supplier<T> action, String errorMsg) {
        try {
            long startTime = System.currentTimeMillis();
            T result = action.get();
            long costTime = System.currentTimeMillis() - startTime;
            // 打印慢查询日志
            if (costTime > SLOW_QUERY_THRESHOLD) {
                log.warn("[慢查询] %s，耗时：%sms", errorMsg.replace("失败", "成功"), costTime);
            }
            return result;
        } catch (Exception e) {
            log.error(String.format("%s，异常详情：", errorMsg), e);
            throw new RepositoryException(errorMsg, e);
        }
    }

    // -------------------------- 通用读操作方法（单表，返回Model） --------------------------
    /**
     * 根据主键查询单条数据（JFinal原生方式：调用Model的dao().findById）
     * @param id 主键ID
     * @return Model对象（不存在返回null）
     */
    public M findById(Object id) {
        return executeReadOperation(() -> modelDao.findById(id),
                String.format("查询%s数据失败，主键ID：%s", modelClass.getSimpleName(), id));
    }

    /**
     * 根据条件查询单条数据（调用Model的dao().findFirst）
     * @param sqlWhere WHERE条件（如：order_no = ? AND status = ?）
     *                注意：无需写SELECT * FROM 表名，直接写WHERE后的条件
     * @param params 条件参数
     * @return Model对象（不存在返回null）
     */
    public M findFirst(String sqlWhere, Object... params) {
        // 拼接完整SQL：Model的findFirst支持直接写WHERE条件，自动拼接表名
        String sql = String.format("SELECT * FROM %s WHERE %s", getTableName(), sqlWhere);
        return executeReadOperation(() -> modelDao.findFirst(sql, params),
                String.format("查询%s单条数据失败，条件：%s，参数：%s", modelClass.getSimpleName(), sqlWhere, params));
    }

    /**
     * 根据条件查询列表数据（调用Model的dao().find）
     * @param sqlWhere WHERE条件
     * @param params 条件参数
     * @return Model列表
     */
    public List<M> findList(String sqlWhere, Object... params) {
        String sql = String.format("SELECT * FROM %s WHERE %s", getTableName(), sqlWhere);
        return executeReadOperation(() -> modelDao.find(sql, params),
                String.format("查询%s列表数据失败，条件：%s，参数：%s", modelClass.getSimpleName(), sqlWhere, params));
    }

    /**
     * 分页查询数据（单表）- 调用JFinal的Db().paginate，返回Model分页
     * @param pageNumber 页码
     * @param pageSize 页大小
     * @param sqlWhere WHERE条件
     * @param params 条件参数
     * @return 分页结果（Model）
     */
    public Page<M> paginate(int pageNumber, int pageSize, String sqlWhere, Object... params) {
        String select = "SELECT *";
        String from = String.format("FROM %s WHERE %s", getTableName(), sqlWhere);
        return executeReadOperation(() -> modelDao.paginate(pageNumber, pageSize, select, from, params), // 将Record分页转换为Model分页
                String.format("分页查询%s数据失败，条件：%s，分页参数：page=%s,size=%s",
                        modelClass.getSimpleName(), sqlWhere, pageNumber, pageSize));
    }

    // -------------------------- 通用读操作方法（多表关联，返回Record） --------------------------
    /**
     * 多表关联查询单条数据
     * @param sql 自定义关联SQL
     * @param params SQL参数
     * @return Record对象（多表结果封装）
     */
    public Record findFirstByJoin(String sql, Object... params) {
        return executeReadOperation(() -> Db.findFirst(sql, params),
                String.format("多表关联查询单条数据失败，SQL：%s，参数：%s", sql, params));
    }

    /**
     * 多表关联查询列表数据
     * @param sql 自定义关联SQL
     * @param params SQL参数
     * @return Record列表
     */
    public List<Record> findListByJoin(String sql, Object... params) {
        return executeReadOperation(() -> Db.find(sql, params),
                String.format("多表关联查询列表数据失败，SQL：%s，参数：%s", sql, params));
    }

    /**
     * 多表关联分页查询
     * @param pageNumber 页码
     * @param pageSize 页大小
     * @param select 查询字段
     * @param from 表关联与条件
     * @param params 参数
     * @return 分页Record结果
     */
    public Page<Record> paginateByJoin(int pageNumber, int pageSize, String select, String from, Object... params) {
        return executeReadOperation(() -> Db.paginate(pageNumber, pageSize, select, from, params),
                String.format("多表关联分页查询失败，select：%s，from：%s，分页参数：page=%s,size=%s",
                        select, from, pageNumber, pageSize));
    }

    /**
     * 统计查询（如COUNT/SUM/AVG）
     * @param sql 统计SQL
     * @param params 参数
     * @return 统计结果（Long类型）
     */
    public Long count(String sql, Object... params) {
        return executeReadOperation(() -> Db.queryLong(sql, params),
                String.format("执行统计查询失败，SQL：%s，参数：%s", sql, params));
    }

    // -------------------------- 工具方法 --------------------------
    /**
     * 获取Model对应的数据库表名（通过Model的dao单例获取）
     * @return 表名
     */
    protected String getTableName() {
        return modelDao.getTableName();
    }
}